#include "../dish.h"
#include "../chef.h"
#include "../judge.h"
#include "test_utilities.h"
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#define ASSERT_NULL(a) ASSERT_TEST((a)==NULL)
#define ASSERT_EQUAL(a,b) ASSERT_TEST((a)==(b))
#define ASSERT_STRING_EQUAL(a,b) ASSERT_TEST(strcmp((a),(b))==0)
#define ASSERT_SUCCESS(a) ASSERT_TEST((a)==JUDGE_SUCCESS)
#define ASSERT_INVALID(a) ASSERT_TEST((a)==JUDGE_INVALID_ARGUMENT)

typedef struct {
	Judge judge;
	Chef chef1;
	Chef chef2;
	Dish inedible;
	Dish edible;
} Examples;

static int taste(Dish dish1,Dish dish2) {
	return dishGetSweetness(dish1)-dishGetSweetness(dish2);
}

static Examples setUp() {
	Examples examples;
	examples.inedible = dishCreate("D1",ENTREE,10,10,10,NULL);
	examples.edible = dishCreate("D2",ENTREE,4,4,4,NULL);
	examples.chef1 = chefCreate("MOSHE", NULL);
	chefAddDish(examples.chef1, examples.inedible, 1, NULL);
	examples.chef2 = chefCreate("AVI", NULL);
	chefAddDish(examples.chef2, examples.edible, 2, NULL);
	examples.judge = judgeCreate("SHLOMO", taste, NULL);
	return examples;
}

void tearDown(Examples examples) {
	judgeDestroy(examples.judge);
	chefDestroy(examples.chef1);
	chefDestroy(examples.chef2);
	dishDestroy(examples.inedible);
	dishDestroy(examples.edible);
}

static bool testJudgeCreate() {
	JudgeResult result;
	Judge judge=judgeCreate(NULL, taste, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(judge);
	judge=judgeCreate("SHLOMO", NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(judge);
	judge=judgeCreate("SHLOMO", taste, &result);
	ASSERT_TEST(judge);
	ASSERT_SUCCESS(result);
	ASSERT_STRING_EQUAL((char*)judgeGetName(judge),"SHLOMO");
	Judge judge2=judgeCreate("SHLOMO", taste, NULL);
	ASSERT_TEST(judge2);
	judgeDestroy(judge);
	judgeDestroy(judge2);
	return true;
}

static bool testJudgeCopy() {
	SET_UP(Examples);
	JudgeResult result;
	Judge judge=judgeCopy(NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_NULL(judge);
	judge=judgeCopy(examples.judge, &result);
	ASSERT_TEST(judge);
	ASSERT_SUCCESS(result);
	ASSERT_STRING_EQUAL((char*)judgeGetName(judge),"SHLOMO");
	Judge judge2=judgeCopy(examples.judge, NULL);
	ASSERT_TEST(judge2);
	judgeDestroy(judge);
	judgeDestroy(judge2);
	TEAR_DOWN();
	return true;
}

static bool testJudgeDestroy() {
	judgeDestroy(NULL);
	Judge judge=judgeCreate("SHLOMO", taste, NULL);
	judgeDestroy(judge);
	return true;
}

static bool testJudgeGetName() {
	SET_UP(Examples);
	const char* name;
	name=judgeGetName(NULL);
	ASSERT_NULL(name);
	name=judgeGetName(examples.judge);
	ASSERT_STRING_EQUAL(name,"SHLOMO");
	TEAR_DOWN();
	return true;
}

static bool testJudgeCompareDishes() {
	SET_UP(Examples);
	JudgeResult result;
	bool whoWon=judgeCompareDishes(NULL, examples.inedible, examples.chef1,
			examples.edible, examples.chef2, &result);
	ASSERT_INVALID(result);
	ASSERT_TEST(!whoWon);
	whoWon=judgeCompareDishes(examples.judge, NULL, examples.chef1,
			examples.edible, examples.chef2, &result);
	ASSERT_INVALID(result);
	ASSERT_TEST(!whoWon);
	whoWon=judgeCompareDishes(examples.judge, examples.inedible, NULL,
			examples.edible, examples.chef2, &result);
	ASSERT_INVALID(result);
	ASSERT_TEST(!whoWon);
	whoWon=judgeCompareDishes(examples.judge, examples.inedible,
			examples.chef1, NULL, examples.chef2, &result);
	ASSERT_INVALID(result);
	ASSERT_TEST(!whoWon);
	whoWon=judgeCompareDishes(examples.judge, examples.inedible,
			examples.chef1, examples.edible, NULL, &result);
	ASSERT_INVALID(result);
	ASSERT_TEST(!whoWon);
	whoWon=judgeCompareDishes(examples.judge, examples.inedible,
			examples.chef1, examples.edible, examples.chef2, &result);
	ASSERT_SUCCESS(result);
	ASSERT_TEST(!whoWon);
	Chef chef=chefCreate("MORDI", NULL);
	Dish dish=dishCreate("D3",ENTREE,5,5,5,NULL);
	chefAddDish(chef, dish, 0, NULL);
	whoWon=judgeCompareDishes(examples.judge, dish, chef, examples.edible,
			examples.chef2, NULL);
	ASSERT_TEST(whoWon);
	whoWon=judgeCompareDishes(examples.judge, dish, chef, dish,
			examples.chef2, NULL);
	ASSERT_TEST(!whoWon);
	chefDestroy(chef);
	dishDestroy(dish);
	TEAR_DOWN();
	return true;
}

static bool testJudgeIsResigning() {
	SET_UP(Examples);
	bool resign=judgeIsResigning(NULL);
	ASSERT_TEST(!resign);
	resign=judgeIsResigning(examples.judge);
	for (int i=0; i<3; i++){
		ASSERT_TEST(!resign);
		resign=judgeCompareDishes(examples.judge, examples.inedible,
				examples.chef1, examples.edible, examples.chef2, NULL);
	}
	resign=judgeIsResigning(examples.judge);
	ASSERT_TEST(resign);
	TEAR_DOWN();
	return true;
}

int main() {
	RUN_TEST(testJudgeCreate);
	RUN_TEST(testJudgeCopy);
	RUN_TEST(testJudgeDestroy);
	RUN_TEST(testJudgeGetName);
	RUN_TEST(testJudgeCompareDishes);
	RUN_TEST(testJudgeIsResigning);
	return 0;
}
